using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CodePlex.Resourceful.Amazon.S3;
using CodePlex.SpaceBlock.FS;
using CodePlex.Resourceful;
using System.Threading;
using System.IO;

namespace CodePlex.SpaceBlock.Test
{
    [TestClass]
    public class FileSystemTests :  BaseTest
    {
        /// <summary>
        ///A test for S3FileSystem
        ///</summary>
        [TestMethod]
        public void Test_S3FileSystem()
        {



            string[] aws = Environment.GetEnvironmentVariable("AWS").Split(':');

            S3Connection connection = new S3Connection(aws[0], aws[1], true, RequestStyle.Path);

            S3FileSystem fs = new S3FileSystem(connection);

            // create bucket
            Path tempBucket = Path.Parse("/asdf.com" + DateTime.Now.Ticks + Path.SeparatorChar);

            try
            {
                fs.EnsureFolderExists(tempBucket);

                // test interface
                this.TestIFileSystem(fs, tempBucket);
            }
            finally
            {
                fs.Delete(tempBucket);
            }



        }

        /// <summary>
        ///A test for LocalFileSystem
        ///</summary>
        [TestMethod]
        public void Test_LocalFileSystem()
        {


            LocalFileSystem fs = new LocalFileSystem();

            Path root = Path.Parse("/C:/in/work/s3/");


            // test interface
            this.TestIFileSystem(fs, root);



            // make sure you cannot create or delete root files or directories

            AssertException(delegate
          {
              fs.EnsureFileExists(Path.Parse("/foo"));
          });

            AssertException(delegate
          {
              fs.EnsureFolderExists(Path.Parse("/foo/"));
          });

            AssertException(delegate
         {
             fs.Delete(Path.Parse(Path.Separator));
         });
            AssertException(delegate
          {
              fs.Delete(Path.Parse("/foo"));
          });

            AssertException(delegate
          {
              fs.Delete(Path.Parse("/foo/"));
          });



        }


        /// <summary>
        ///A test for NirvanixFileSystem
        ///</summary>
        [TestMethod]
        public void Test_NirvanixFileSystem()
        {



            string[] nirvanix = Environment.GetEnvironmentVariable("NIRVANIX").Split(':');


            string appKey = nirvanix[0];
            string appName = nirvanix[1];
            string username = nirvanix[2];
            string password = nirvanix[3];


            NirvanixFileSystem fs = new NirvanixFileSystem(appKey, appName, username, password, true);
            fs.Tracer.AddListener(Console.Out);

            // create bucket
            Path tempFolder = Path.Parse("/asdf.com" + DateTime.Now.Ticks + Path.SeparatorChar);

            try
            {
                fs.EnsureFolderExists(tempFolder);

                // test interface
                this.TestIFileSystem(fs, tempFolder);
            }
            finally
            {
                fs.Delete(tempFolder);
            }



        }


        private void TestIFileSystem(IFileSystem fs, Path root)
        {


            // set up

            Path working = root.Combine("work" + DateTime.Now.Ticks + Path.SeparatorChar);

            try
            {
                fs.EnsureFolderExists(working);


                // run tests

                // folder tests
                Path newFolder = working.Combine("newFolder/");

                fs.EnsureFolderExists(newFolder);


                Assert.IsTrue(fs.Exists(newFolder));

                Assert.IsFalse(fs.Exists(working.Combine("bogus")));



                fs.Delete(newFolder);
                Assert.IsFalse(fs.Exists(newFolder));





                // get items
                Assert.AreEqual(0, fs.GetItems(working).Count);

                fs.EnsureFolderExists(newFolder);



                Assert.AreEqual(1, fs.GetItems(working).Count);

                Assert.IsInstanceOfType(fs.GetItems(working)[0], typeof(IFolderItem));


                Utils.TraceWriteLine("Last modified is {0}.", fs.GetItems(working)[0].LastModified);


                Assert.AreEqual(1, fs.GetFolders(working).Count);
                Assert.AreEqual(0, fs.GetFiles(working).Count);



                // file test

                Path newFile = working.Combine("foo.txt");
                fs.EnsureFileExists(newFile);
                Assert.AreEqual(1, fs.GetFiles(working).Count);

                Assert.IsTrue(fs.GetFiles(working)[0].Size <= 1L);  // some fs don't allow 0 byte files


                // make sure it doesn't overwrite
                DateTime? newFileModified = fs.GetFiles(working)[0].LastModified;
                Assert.AreEqual(2, fs.GetItems(working).Count);
                fs.EnsureFileExists(newFile);
                Assert.AreEqual(2, fs.GetItems(working).Count);
                Assert.AreEqual(newFileModified.Value.Ticks, fs.GetFiles(working)[0].LastModified.Value.Ticks);


                // check that modified times are different
                Thread.Sleep(500);

                fs.Delete(newFile);
                Assert.AreEqual(0, fs.GetFiles(working).Count);
                fs.EnsureFileExists(newFile);
                Assert.AreNotEqual(newFileModified.Value.Ticks, fs.GetFiles(working)[0].LastModified.Value.Ticks);

                // get item

                Assert.IsInstanceOfType(fs.GetItem(newFile), typeof(IFileItem));
                Assert.IsNull(fs.GetItem(working.Combine("bogus")));
                Assert.IsNull(fs.GetItem(working.Combine("bogus/")));


                // transfer
                Path actualFile = working.Combine("actualfile");
                Assert.IsNull(fs.GetItem(actualFile));
                MockFileItem source = new MockFileItem();
                source.Bytes = Encoding.UTF8.GetBytes("actual file contents");
                fs.Transfer(source, actualFile, null);
                IFileItem actualFile2 = fs.GetItem(actualFile) as IFileItem;
                Assert.IsNotNull(actualFile2);
                Assert.AreEqual(source.Size, actualFile2.Size);
                Assert.AreEqual(actualFile, actualFile2.Path);
                AssertAreEqual(source.Bytes, ReadBytes(actualFile2, source.Bytes.Length));
                AssertAreEqual(Utils.ToMD5(source.Bytes), actualFile2.QuickMD5);
                fs.Delete(actualFile);

                // transfer with metadata
                ISupportsTransferWithMetadata twm = fs as ISupportsTransferWithMetadata;
                if (twm != null)
                {
                    NameValuePairs m = new NameValuePairs();
                    m[S3Constants.Headers.AmazonUserMetadataPrefix + "a"] = "a value";
                    m[S3Constants.Headers.AmazonUserMetadataPrefix + "b"] = "b value";


                    Path actualFileM = working.Combine("actualfilem");
                    Assert.IsNull(fs.GetItem(actualFileM));
                    source.Bytes = Encoding.UTF8.GetBytes("actual file contents M");
                    twm.Transfer(source, actualFileM, m, null);
                    IFileItem actualFile3 = fs.GetItem(actualFileM) as IFileItem;
                    AssertAreEqual(Utils.ToMD5(source.Bytes), actualFile3.QuickMD5);
                    Assert.IsNotNull(actualFile3);
                    Assert.AreEqual(source.Size, actualFile3.Size);
                    Assert.AreEqual(actualFileM, actualFile3.Path);
                    AssertAreEqual(source.Bytes, ReadBytes(actualFile3,source.Bytes.Length));

                    Metadata m2 = fs.GetMetadata(actualFileM);
                    Assert.IsNotNull(m2);
                    foreach (string key in m.Keys)
                    {
                        Assert.AreEqual(m[key], m2[key]);
                    }

                    fs.Delete(actualFileM);


                }





                // get acl

                //ACL acl = fs.GetACL(newFile);


                //Utils.TraceWriteLine(acl.ToString());


            }
            finally
            {
                // clean up
                fs.Delete(working);

            }
        }

        private byte[] ReadBytes(IFileItem file, int count)
        {

            using (Stream stream = file.GetInputStream())
            {
                BinaryReader reader = new BinaryReader(stream);

                return reader.ReadBytes(count);
            }
            //try
            //{
              
            //}
            //finally
            //{
            //    stream.Close();
            //    stream.Dispose();
            //    stream = null;
            //}
        }


        private class MockFileItem : IFileItem
        {
            private byte[] _bytes;

            public byte[] Bytes 
            {
                get
                {
                    return _bytes;
                }
                set
                {
                    _bytes = value;
                }
            }

            public long Size
            {
                get { return _bytes.LongLength; }
            }

            public byte[] QuickMD5
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public System.IO.Stream GetInputStream()
            {
                return new MemoryStream(this.Bytes);
            }

         

            #region IPathItem Members

            public IFileSystem FileSystem
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Path Path
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public DateTime? LastModified
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            #endregion
        }

    }
}
